package com.tedu.openmedical.medicalsystem.user.service.impl;

import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tedu.openmedical.base.exception.ServiceException;
import com.tedu.openmedical.base.exception.UsernameErrorException;
import com.tedu.openmedical.base.exception.VerificationCodeErrorException;
import com.tedu.openmedical.base.response.StatusCode;
import com.tedu.openmedical.medicalsystem.base.utils.*;
import com.tedu.openmedical.medicalsystem.send.mapper.SendVerMapper;
import com.tedu.openmedical.medicalsystem.send.pojo.entity.VerificationCode;
import com.tedu.openmedical.medicalsystem.send.pojo.vo.VerificationCodeVO;
import com.tedu.openmedical.medicalsystem.logininfo.mapper.LoginInfoMapper;
import com.tedu.openmedical.medicalsystem.user.mapper.UserMapper;
import com.tedu.openmedical.medicalsystem.user.pojo.dto.UserLoginParam;
import com.tedu.openmedical.medicalsystem.logininfo.pojo.entity.LoginInfo;
import com.tedu.openmedical.medicalsystem.user.pojo.entity.User;
import com.tedu.openmedical.medicalsystem.user.service.UserService;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.*;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private SendVerMapper sendVerMapper;

    @Autowired
    private LoginInfoMapper loginInfoMapper;

    /**
     * 根据用户名登录
     * @param userLoginParam 登录参数
     */
    @Override
    public Map<String, Object> login(UserLoginParam userLoginParam, HttpServletRequest httpServletRequest) throws ServiceException {

        String loginType = userLoginParam.getLoginType();

        if ("phone".equals(loginType)) {
            // 手机号登录
            return loginByPhone(userLoginParam, httpServletRequest);
        }else if ("username".equals(loginType)){
            // 用户名登录
            return loginByUsername(userLoginParam, httpServletRequest);
        }
        throw new IllegalArgumentException("登录类型错误");
    }

    @Override
    public List<User> selectAll() {
        List<User> users = userMapper.selectList(new QueryWrapper<>());
        log.debug("users: {}", users);
        return users;
    }

    private Map<String, Object> loginByUsername(UserLoginParam userLoginParam, HttpServletRequest httpServletRequest) {
        Map<String, Object> result = new HashMap<>();
        Subject subject = SecurityUtils.getSubject();
        // 封装 loginInfo 对象
        LoginInfo loginInfo = createLoginInfo(httpServletRequest);
        loginInfo.setLoginAccount(userLoginParam.getUserName());
        loginInfo.setUserName(userLoginParam.getUserName());
        try {
            UsernamePasswordToken authToken = new UsernamePasswordToken(userLoginParam.getUserName(), userLoginParam.getPassword());
            subject.login(authToken);
            result.put("user", ShiroSecurityUtils.getCurrentSimpleUser());
            result.put("token", JwtUtils.createToken(userLoginParam));
            LoginSuccess(loginInfo);
        } catch (AuthenticationException | UnsupportedEncodingException e) {
            log.error("登录失败: {}", e.getMessage());
            LoginFail(loginInfo);
            throw new ServiceException(StatusCode.USERNAME_ERROR);
        }
        return result;
    }


    /**
     * 根据手机号登录
     * @param userLoginParam 登录参数
     */
    private Map<String, Object> loginByPhone(UserLoginParam userLoginParam, HttpServletRequest httpServletRequest) {

        Map<String, Object> result = new HashMap<>();
        //使用MybatisPlus的查询条件构造器来构造查询条件
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        //查询条件为手机号,eq表示相等
        String phone = userLoginParam.getPhone();
        wrapper.eq(User.COL_PHONE, phone);

        // 查询用户是否存在
        User user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new IllegalArgumentException("手机号不存在");
        }

        // 查询验证码是否正确
        VerificationCodeVO verificationCodeVO = sendVerMapper.selectVerCode(phone);
        Integer verCode = verificationCodeVO.getVerificationCode();
        log.debug("verCode: {}", verCode);
        LoginInfo loginInfo = createLoginInfo(httpServletRequest);
        loginInfo.setLoginAccount(userLoginParam.getPhone());
        if (Objects.equals(verCode, userLoginParam.getVerificationCode())) {
            // 验证码正确, 登录成功
            VerificationCode verificationCode = new VerificationCode();
            verificationCode.setIsCheck((byte) 1);
            sendVerMapper.update(verificationCode);
            result.put("user", ShiroSecurityUtils.getCurrentSimpleUser());
            try {
                result.put("token", JwtUtils.createToken(userLoginParam));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            LoginSuccess(loginInfo);
        } else {
            LoginFail(loginInfo);
            throw new VerificationCodeErrorException(StatusCode.VERIFICATION_CODE_ERROR);
        }
        return result;
    }

    @Override
    public User queryUserByPhone(String phone) {
        User user = userMapper.selectOne(new QueryWrapper<User>().eq(User.COL_PHONE, phone));
        if (user == null) {
            // 根据业务需求处理用户不存在的情况
            throw new ServiceException(StatusCode.USERNAME_ERROR);
        }
        return user;
    }

    @Override
    public User findByUsername(String userName) {
        User user = userMapper.selectOne(new QueryWrapper<User>().eq(User.COL_USER_NAME, userName));
        if (user == null) {
            // 根据业务需求处理用户不存在的情况
            throw new ServiceException(StatusCode.USERNAME_ERROR);
        }
        return user;
    }



    /**
     * 创建登录对象
     * @param httpServletRequest 请求对象
     * @return 登录日志对象
     */
    private LoginInfo createLoginInfo(HttpServletRequest httpServletRequest) {
        LoginInfo loginInfo = new LoginInfo();
        UserAgent userAgent = UserAgent.parseUserAgentString(httpServletRequest.getHeader("User-Agent"));
        //获取IP地址
        String ipAddr = IpUtils.getIpAddr(httpServletRequest);
        //获取操作系统
        String osName = userAgent.getOperatingSystem().getName();
        //获取浏览器类型
        String browser = userAgent.getBrowser().getName();
        //获取登录地址
        String location = AddressUtils.getRealAddressByIP(ipAddr);

        loginInfo.setIpAddr(ipAddr);
        loginInfo.setLoginLocation(location);
        loginInfo.setOs(osName);
        loginInfo.setBrowser(browser);
        loginInfo.setLoginTime(DateUtil.date());
        loginInfo.setLoginType("0");
        return loginInfo;
    }

    private void LoginSuccess(LoginInfo loginInfo){
        loginInfo.setMsg("登录成功");
        loginInfo.setLoginStatus("0");
        loginInfo.setUserName(ShiroSecurityUtils.getCurrentSimpleUser().getUsername());
        loginInfoMapper.insertLoginInfo(loginInfo); // 插入登录信息
    }

    private void LoginFail(LoginInfo loginInfo){
        loginInfo.setMsg("用户名或密码不正确");
        loginInfo.setLoginStatus("1");
        loginInfoMapper.insertLoginInfo(loginInfo);
    }
}
